#pragma once
#include <spdlog/spdlog.h>
#include <vsg/all.h>
#include <entt/entt.hpp>
#include "layer_type.h"
#include "qlib/all.h"

typedef std::vector<std::string> (*GetLayerType)(long long);

namespace qbe::utils::TempRender {

struct Line_Uniform {
  vsg::vec4 color;
  vsg::vec2 canvas;
  float dash;
  float gap;
  bool isDash;
};
struct Fill_Uniform {
  vsg::vec4 color;
  float width;
  float space;
  float line_width;
  float dot;
  float angle;
  float scale;
  float only_side;
  float use_point_grid;
  int useTexture;
  int use_overlapping_discoloration;
};

struct Point_Uniform {
  vsg::vec4 color;
  int point_type;
  float point_size;
};

struct Grid_Uniform {
  vsg::vec2 transform_p;
  vsg::vec2 loop_p;
  vsg::vec4 bg_color;
  vsg::vec4 axis_color;
  vsg::vec4 line_color;
  float width;
  float height;
  float step_v;
  float device_ratio;
  int show_grid;
  int show_point;
};
struct LineShaderMaterial2D {
  Line_Uniform uniform;
  vsg::Path vertexShader;
  vsg::Path fragmentShader;
};

struct FillShaderMaterial2D {
  Fill_Uniform uniform;
  vsg::Path vertexShader;
  vsg::Path fragmentShader;
};

struct PointShaderMaterial2D {
  Point_Uniform uniform;
  vsg::Path vertexShader;
  vsg::Path fragmentShader;
};

struct GridShaderMaterial2D {
  Grid_Uniform uniform;
  vsg::Path vertexShader;
  vsg::Path fragmentShader;
};

struct Mesh2D {
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  std::vector<uint32_t> tri_indices;

  bool is_dirty{false};

  Mesh2D();
  Mesh2D(uint32_t size);

  Mesh2D(vsg::ref_ptr<vsg::vec2Array> _vertices);

  Mesh2D(vsg::ref_ptr<vsg::vec2Array> _vertices, vsg::ref_ptr<vsg::uintArray> _indices);

  Mesh2D(qlib::Vec2dArray points, bool connect = false, bool triangle = false);

  void gen_from_points(qlib::Vec2dArray points, bool connect = false);
  void gen_tri_indices(qlib::Vec2dArray points);

  void update(vsg::ref_ptr<vsg::vec2Array> _vertices);

  void print();

  void dirty();
};

void mesh_merge(const std::vector<Mesh2D>& in_meshs, Mesh2D& out_meshs);
using Mesh2DArray = std::vector<Mesh2D>;

class LineMeshNode : public vsg::Inherit<vsg::StateGroup, LineMeshNode> {

 public:
  vsg::ref_ptr<vsg::Options> options;

  Mesh2D mesh;
  LineShaderMaterial2D material;

  qlib::Tag tag;

  VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP};

  vsg::ref_ptr<vsg::Value<Line_Uniform>> uniform;

  GetLayerType m_layer_type_func = nullptr;

 public:
  LineMeshNode();
  LineMeshNode(vsg::ref_ptr<vsg::Options> _options, qlib::Tag _tag, Mesh2D _mesh, LineShaderMaterial2D _material,
               VkPrimitiveTopology _primitiveTopology = VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
  ~LineMeshNode();

  template <typename... Args>
  static vsg::ref_ptr<LineMeshNode> create(Args&&... args) {
    return vsg::ref_ptr<LineMeshNode>(new LineMeshNode(std::forward<Args>(args)...));
  }

  void addPipe();

  void clear();

  void addCommands();

  void update();

  void updateLayerType();
  void setLayerType(LayerParam& param);
  void setLayerType(vsg::vec4 color, std::string line_type);
};

class FillMeshNode : public vsg::Inherit<vsg::StateGroup, FillMeshNode> {

 public:
  vsg::ref_ptr<vsg::Options> options;

  Mesh2D mesh;
  FillShaderMaterial2D material;

  VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP};

 public:
  FillMeshNode();

  FillMeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, FillShaderMaterial2D _material,
               VkPrimitiveTopology _primitiveTopology = VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
  ~FillMeshNode();

  template <typename... Args>
  static vsg::ref_ptr<FillMeshNode> create(Args&&... args) {
    return vsg::ref_ptr<FillMeshNode>(new FillMeshNode(std::forward<Args>(args)...));
  }

  void addPipe();

  void clear();

  void addCommands();

  void addMesh(const Mesh2D& mh);

  void update();

  void addCmd(const std::vector<Mesh2D>& meshs);

  void addMeshs(const std::vector<Mesh2D>& meshs);

  void updateMesh(const Mesh2D& mh);

  void updateMeshs(const std::vector<Mesh2D>& meshs);
};

class PointMeshNode : public vsg::Inherit<vsg::StateGroup, PointMeshNode> {

 public:
  vsg::ref_ptr<vsg::Options> options;

  Mesh2D mesh;
  PointShaderMaterial2D material;
  vsg::ref_ptr<vsg::Value<Point_Uniform>> uniform;

  VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP};

 public:
  PointMeshNode();

  PointMeshNode(vsg::ref_ptr<vsg::Options> _options, Mesh2D _mesh, PointShaderMaterial2D _material,
                VkPrimitiveTopology _primitiveTopology = VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
  ~PointMeshNode();

  template <typename... Args>
  static vsg::ref_ptr<PointMeshNode> create(Args&&... args) {
    return vsg::ref_ptr<PointMeshNode>(new PointMeshNode(std::forward<Args>(args)...));
  }

  void addPipe();

  void clear();

  void addCommands();

  void addMesh(const Mesh2D& mh);

  void update();
  void addCmd(const std::vector<Mesh2D>& meshs);

  void addMeshs(const std::vector<Mesh2D>& meshs);

  void updateMesh(const Mesh2D& mh);

  void updateMeshs(const std::vector<Mesh2D>& meshs);
  void setLayerType(vsg::vec4 color, int point_type, float point_size);
};

class GridNode : public vsg::Inherit<vsg::StateGroup, GridNode> {

 public:
  vsg::ref_ptr<vsg::Options> options;
  GridShaderMaterial2D material;

  VkPrimitiveTopology primitiveTopology{VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST};
  float width = 1316;
  float height = 976;
  vsg::ref_ptr<vsg::Value<Grid_Uniform>> uniform;

 public:
  GridNode(vsg::ref_ptr<vsg::Options> _options, GridShaderMaterial2D _material);
  ~GridNode();
  void setGridParam(double w, double h, vsg::dvec2 tran, double step, vsg::vec4 bg_color, vsg::vec4 grid_color, bool show_grid, bool show_point);

  template <typename... Args>
  static vsg::ref_ptr<GridNode> create(Args&&... args) {
    return vsg::ref_ptr<GridNode>(new GridNode(std::forward<Args>(args)...));
  }

  void addPipe();

  void addCommands();
};

}  // namespace qbe::utils::TempRender